﻿namespace EFS.RC.CC.Applications.EndPoints.OfficeService
{
	/// <summary>
	/// Operators End Point
	/// </summary>
	internal class Operators : EFS.RC.CC.Applications.EndPoints.Presence<EFS.RC.CC.Deployment.Entities.EndPoints.Office, EFS.RC.CC.Presences.EndPoints.OfficeService.Operator>
	{
		#region Composition

		// Agents trackers
		private EFS.RC.CC.Trackers.OfficeService.Remote.Applications remoteApplicationsTracker = null;

		// Team
		private EFS.RC.CC.Presences.Operators.OfficeService.Team operatorsTeam = null;

		// Team tracker
		private EFS.RC.CC.Trackers.Team.Presences presencesTracker = null;

		// Deployment watch
		private EFS.RC.CC.Tools.Deployment.Watch watch = null;
		private System.DateTime? lastDomainUpdate = null;
		private System.DateTime? lastDeploymentLoad = null;

		private EFS.AD.Domain domain = null;

		#endregion

		#region Delegates

		/// <summary>
		/// Deployment changes watch
		/// </summary>
		public delegate void DeploymentLoad(EFS.RC.CC.Applications.EndPoints.OfficeService.Operators operatorsOfficeServiceEndPointApplication);

		/// <summary>
		/// Deployment watch delegate registrar
		/// </summary>
		public DeploymentLoad OnDeploymentLoad;

		#endregion

		#region Constructor

		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="ownerUri"></param>
		public Operators(
			Microsoft.Rtc.Collaboration.ApplicationEndpointSettings applicationEndpointSettings,
			EFS.RC.CC.Presences.EndPoints.OfficeService.Operator operatorsEndPointPresence)
			: base(applicationEndpointSettings, operatorsEndPointPresence)
		{
			// domain
			domain = new AD.Domain();

			// make local list of agents
			operatorsTeam = new EFS.RC.CC.Presences.Operators.OfficeService.Team(this);

			// make agents status tracker
			remoteApplicationsTracker = new EFS.RC.CC.Trackers.OfficeService.Remote.Applications(operatorsTeam);

			// presences tracker
			presencesTracker = new EFS.RC.CC.Trackers.Team.Presences(operatorsTeam);

			// make deployment watch
			watch = new EFS.RC.CC.Tools.Deployment.Watch(this);
			watch.OnWatchDeployment += WatchDeployment;

			// update domain users
			UpdateDomainUsers();

			// load deployment
			LoadDeployment();
		}

		#endregion

		#region Usage

		/// <summary>
		/// Describe this object
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CC.Applications.EndPoints.OfficeService.Operators [{0}]", base.Describe());
		}

		/// <summary>
		/// Operators team
		/// </summary>
		public EFS.RC.CC.Presences.Operators.OfficeService.Team OperatorsTeam
		{
			get { return operatorsTeam; }
		}

		/// <summary>
		/// Tracher
		/// </summary>
		public EFS.RC.CC.Trackers.OfficeService.Remote.Applications OfficeServiceTracker
		{
			get { return remoteApplicationsTracker; }
		}

		/// <summary>
		/// Domain
		/// </summary>
		public EFS.AD.Domain Domain
		{
			get
			{
				return domain;
			}
		}

		/// <summary>
		/// Get status message
		/// </summary>
		/// <returns></returns>
		public System.String StatusDescription()
		{
			// update status
			return System.String.Format("Last End Point reload: {0} \n Last Domain update: {1}" ,
				lastDeploymentLoad,
				lastDomainUpdate); 
		}

		/// <summary>
		/// Get status message
		/// </summary>
		/// <returns></returns>
		public void ResetReloadTime()
		{
			// update status
			lastDomainUpdate = null;
			lastDeploymentLoad = null;
		}

		#endregion

		#region Startup

		/// <summary>
		/// End point is now active, register agents
		/// </summary>
		/// <param name="collaborationApplicationEndpoint"></param>
		protected override void PostEndPointEstablish(Microsoft.Rtc.Collaboration.ApplicationEndpoint collaborationApplicationEndpoint)
		{
			// log
			EFS.EventLog.Log.LogInfo("PostEndPointEstablish", this);

			// check
			EFS.Common.Verify.Reference(collaborationApplicationEndpoint, "collaborationApplicationEndpoint");

			// make presences tracker
			presencesTracker.BeginStartup();

			// remote app tracker
			remoteApplicationsTracker.BeginStartup();

			// deployment watch
			watch.BeginStartup();

			// register for calls
			collaborationApplicationEndpoint.RegisterForIncomingCall<Microsoft.Rtc.Collaboration.InstantMessagingCall>(OnIncomingInstantMessagingCalls);
		}

		#endregion

		#region Runtime

		/// <summary>
		/// Incomming messages
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void OnIncomingInstantMessagingCalls(object sender, Microsoft.Rtc.Collaboration.CallReceivedEventArgs<Microsoft.Rtc.Collaboration.InstantMessagingCall> args)
		{
			// log
			EFS.EventLog.Log.LogInfo(System.String.Format("OnIncomingInstantMessagingCalls {0}", args.RemoteParticipant.Uri), this);

			try
			{
				// SAL
				EFS.RC.CC.Tools.Loggers.SAL.Add(null, "OnIncomingInstantMessagingCalls", args.Call.RemoteEndpoint.Participant.Uri, this); 

				// check if new conversation
				if (args.IsNewConversation && args.CallToBeReplaced == null)
				{
					// check toast mesage
					if (args.ToastMessage.Message == "SUPERVISOR CENTRAL")
					{
						// get supervisor
						EFS.RC.CC.Presences.Operators.OfficeService.Supervisors.Supervisor supervisorPresence = OperatorsTeam.GetSupervisor(args.RemoteParticipant.Uri);

						// check if in this team
						if (null != supervisorPresence)
						{
							// make session
							supervisorPresence.StartSupervisorCentralApplication(args.Call, OperatorsTeam);
						}
						else
						{
							args.Call.Decline();
						}
					}
					else // check toast mesage
					if (args.ToastMessage.Message == "WALL BOARD")
					{
						// new
						EFS.RC.CC.Applications.Remote.WallBoard.Monitor wallBoardMonitor = new EFS.RC.CC.Applications.Remote.WallBoard.Monitor(args.Call, this);

						// start
						wallBoardMonitor.BeginStartup();
					}
					else // check toast mesage
					if (args.ToastMessage.Message == "start talking")
					{
						// annon
						EFS.RC.CC.Presences.Operators.Anonymous annonimousPresence = new EFS.RC.CC.Presences.Operators.Anonymous(args.RemoteParticipant.Uri);

						// new
						EFS.RC.CC.Applications.Remote.Operators.SuperUser superUser = new EFS.RC.CC.Applications.Remote.Operators.SuperUser(annonimousPresence, args.Call);

						// start
						superUser.BeginStartup();
					}
					else
					{
						args.Call.Decline();
					}				}
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);

				try
				{
					// decline call
					args.Call.Decline();
				}
				catch (System.Exception ex1)
				{
					// log
					EFS.EventLog.Log.LogException(ex1, this);
				}
			}
		}

		/// <summary>
		/// Update Domain
		/// </summary>
		/// <returns></returns>
		private void UpdateDomain()
		{
			// log
			EFS.EventLog.Log.LogInfo("UpdateDomain", this);

			// check last update
			if (null == lastDomainUpdate || System.DateTime.Now.Day != lastDomainUpdate.Value.Day)
			{
				// reload domain
				domain = new AD.Domain();

				// update users database
				UpdateDomainUsers();
			}
		}

		/// <summary>
		/// Update Deployment
		/// </summary>
		/// <returns></returns>
		private void UpdateDeployment(EFS.RC.CC.Deployment.Entities.EndPoints.Office officeEndPoint)
		{
			// log
			EFS.EventLog.Log.LogInfo("UpdateDeployment", this);

			// check
			EFS.Common.Verify.Reference(officeEndPoint, "officeEndPoint");

			// check last update time
			if (null == lastDeploymentLoad || lastDeploymentLoad < officeEndPoint.ChangedOn)
			{
				// keep
				lastDeploymentLoad = System.DateTime.Now;

				// load deployment
				LoadDeployment();
			}
		}

		/// <summary>
		/// Reload
		/// </summary>
		/// <returns></returns>
		private void WatchDeployment()
		{
			// log
			EFS.EventLog.Log.LogInfo("WatchDeployment", this);

			// reload end point deployment
			EFS.RC.CC.Deployment.Entities.EndPoints.Office officeEndPoint = EFS.RC.CC.Deployment.Tables.Tools.OfficeEndPoints.GetByID(ApplicationPresence.PresenceEndPointDeployment.ID.Value);
			if (null == officeEndPoint)
			{
				// shut down
				BeginShutdown();

				// done
				return;
			}

			// check uri
			if (!EFS.RC.Common.Utilities.SipUriCompare(ApplicationPresence.PresenceEndPointDeployment.URI, officeEndPoint.URI))
			{
				// shut down
				BeginShutdown();

				// done
				return;
			}

			// update domain
			UpdateDomain();

			// update deployment
			UpdateDeployment(officeEndPoint);
		}

		/// <summary>
		/// Update Users
		/// </summary>
		/// <returns></returns>
		private void UpdateDomainUsers()
		{
			// log
			EFS.EventLog.Log.LogInfo("UpdateDomainUsers", this);

			try
			{
				// update domain
				lastDomainUpdate = System.DateTime.Now;

				// update -> create
				EFS.RC.CC.Tools.Deployment.Update.DomainUsers updateDomainUsers = new EFS.RC.CC.Tools.Deployment.Update.DomainUsers(domain);

				// execute database update
				updateDomainUsers.Execute();
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		/// <summary>
		/// Reload
		/// </summary>
		/// <returns></returns>
		private void LoadDeployment()
		{
			// log
			EFS.EventLog.Log.LogInfo("LoadDeployment", this);

			// keep time
			lastDeploymentLoad = System.DateTime.Now; 

			// reload deployment for all custome end points first
			ApplicationPresence.EndPointsPresencesRegistrar.ForeachCustomerOfficeServicePresence(ReloadCustomerEndPointApplicationDeployment);

			// move to team
			if (null != OnDeploymentLoad)
			{
				try
				{
					OnDeploymentLoad(this);
				}
				catch (System.Exception ex)
				{
					// handle exception
					HandleExceptionSafe(ex);
				}
			}
		}

		/// <summary>
		/// Delaod customer end point deployment
		/// </summary>
		/// <param name="customerOfficeServiceEndPointPresence"></param>
		private void ReloadCustomerEndPointApplicationDeployment(EFS.RC.CC.Presences.EndPoints.OfficeService.Customer customerOfficeServiceEndPointPresence)
		{
			// log
			EFS.EventLog.Log.LogInfo("ReloadCustomerEndPointApplicationDeployment", this);

			//check
			EFS.Common.Verify.Reference(customerOfficeServiceEndPointPresence, "customerOfficeServiceEndPointPresence");

			// get app
			EFS.RC.CC.Applications.EndPoints.OfficeService.Customers customerOfficeServiceEndPointApplication = null;

			// get app
			if (customerOfficeServiceEndPointPresence.TryGetPresenceEndPointApplication(ref customerOfficeServiceEndPointApplication))
			{
				customerOfficeServiceEndPointApplication.ReloadDeployment();
			}
		}
		#endregion

		#region Shutdown

		/// <summary>
		/// Clean-up 
		/// </summary>
		/// <param name="collaborationApplicationEndpoint"></param>
		protected override void PreEndPointTerminate(Microsoft.Rtc.Collaboration.ApplicationEndpoint collaborationApplicationEndpoint)
		{
			// log
			EFS.EventLog.Log.LogInfo("PreEndPointTerminate", this);

			//check
			EFS.Common.Verify.Reference(collaborationApplicationEndpoint, "collaborationApplicationEndpoint");

			// wait list
			EFS.Common.Async.AsyncWaitList asyncWaitList = new EFS.Common.Async.AsyncWaitList();

			// unregister
			collaborationApplicationEndpoint.UnregisterForIncomingCall<Microsoft.Rtc.Collaboration.InstantMessagingCall>(OnIncomingInstantMessagingCalls);

			// shut down deployment watch
			if (null != watch && watch.Status != EFS.Common.Async.RepeaterSlot.SlotStatus.Terminated)
			{
				asyncWaitList.Add(watch.BeginShutdown());
			}

			// shut down remote applications tracker
			if (null != remoteApplicationsTracker && remoteApplicationsTracker.Status != Trackers.OfficeService.Remote.Applications.SlotStatus.Terminated)
			{
				asyncWaitList.Add(remoteApplicationsTracker.BeginShutdown());
			}

			// shut down remote applications tracker
			if (null != presencesTracker && presencesTracker.Status != Trackers.Team.Presences.TrackerStatus.Terminated)
			{
				asyncWaitList.Add(presencesTracker.BeginShutdown());
			}

			// wait
			asyncWaitList.WaitAll();
		}

		/// <summary>
		/// Stopped 
		/// </summary>
		/// <param name="collaborationApplicationEndpoint"></param>
		protected override void EndPointStopped()
		{
			// log
			EFS.EventLog.Log.LogInfo("EndPointStopped", this);
		}

		#endregion
	}
}
